﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using Swift;
using Swift.Math;
using SCM;

public class Room4Client : Room
{
    ServerPort sp;
    BattleReplayer replayer;

    public static event Action<string, string, Vec2, bool> OnBeforeBattleBegin = null;
    public static event Action<Room4Client, bool> OnBattleBegin = null;
    public static event Action<Room, string, bool> OnBattleEnd = null;
    public static event Action<Room4Client> OnFrameElapsed = null;
    public static event Action<Vec2[]> NotifyAddObstacle = null;
    public static event Action<Unit, Unit> NotifyAddBattleUnit = null;
    public static event Action<Unit> OnSetPath = null;
    public static event Action<Unit> NotifyAddBuildingUnit = null;
    public static event Action<Unit> NotifyBuildingLevelUp = null;
    public static event Action<Unit> OnConstructingCompleted = null;
    public static event Action<int, string, Fix64> OnResourceChanged = null;
    public static event Action<Unit, string, Fix64> OnResourceProduced = null;
    public static event Action<Unit, string> OnStartConstructingBattleUnit = null;
    public static event Action<Unit, string> OnConstructingWaitingListChanged = null;
    public static event Action<Unit> NotifyUnitRemoved = null;
    public static event Action<Unit, Unit> OnDoAttack = null;

    public static event Action OnUsrsSwitched = null;

    // 当前执行的消息队列
    Queue<KeyValuePair<string, Action>> msgQ = new Queue<KeyValuePair<string, Action>>();

    // 寻路实现
    public Func<Vec2, Vec2, Fix64, Vec2[]> PathFinder = null;

    public void Init()
    {
        sp = GameCore.Instance.Get<ServerPort>();
        replayer = new BattleReplayer();
        GameCore.Instance.Add("Replayer", replayer);
        GameCore.Instance.Room = this;

        OnMessageDirectly("BattleBegin", OnBattleBeginMsg);
        OnMessageDirectly("FF", OnFrameMoveForwardMsg);
        OnMessageDirectly("BattleEnd", OnBattleEndMsg);

        BufferMessage("SetPath", OnSetPathMsg);
        BufferMessage("CosntructBuildingUnit", OnCosntructBuildingUnitMsg);
        BufferMessage("CancelBuilding", OnCancelBuilding);
        BufferMessage("BuildingLevelUp", OnBuildingLevelUpMsg);
        BufferMessage("ConstructBattleUnit", OnConstructBattleUnitMsg);
        BufferMessage("CosntructAccessoryUnit", OnConstructAccessaryUnit);
        BufferMessage("CheatCode", OnCheatCodeMsg);
        BufferMessage("DropSoldierFromCarrier", OnDropSoldierFromCarrierMsg);
        BufferMessage("AddUnitAt", OnAddUnitAtMsg);
        BufferMessage("PlayerSwitched", OnPlayersSwitchedMsg);
        BufferMessage("ReleaseBattleUnits", OnReleaseBattleUnitsMsg);

        sp.OnRequest("FindPath", OnSrvFindPath);
    }

    public UnitConfigInfo GetMyUnitConfig(string type)
    {
        return GetPlayerUnitConfig(GameCore.Instance.MePlayer, type);
    }

    // 是否存在满足指定条件的单位
    public bool ExistsMyUnit(Func<Unit, bool> filter)
    {
        foreach (var u in AllUnits)
        {
            if (u.Player == GameCore.Instance.MePlayer && filter(u))
                return true;
        }

        return false;
    }

    // 是否存在满足指定条件的单位
    public bool ExistsMyUnit(string unitType)
    {
        return ExistsMyUnit((u) => u.UnitType == unitType);
    }

    // 服务器寻路请求
    void OnSrvFindPath(IReadableBuffer data, IWriteableBuffer buff)
    {
        var src = data.ReadVec2();
        var dst = data.ReadVec2();
        var size = data.ReadFix64();

        Vec2[] path = PathFinder(src, dst, size);
        buff.Write(path);
    }

    // 立即响应的消息
    void OnMessageDirectly(string op, Action<IReadableBuffer> cb)
    {
        sp.OnMessage(op, (data) =>
        {
            replayer.Record(op, data);
            cb.SC(data);
        });

        replayer.OnMessage(op, cb);
    }

    ulong frameNoRecieved = 0; // 消息已经接收到了第几帧

    // 已经收到数据的帧编号和本地数据进度的帧编号的差值，如果太大，则应该加速推进
    public int WaitingFrameCount { get { return (int)(frameNoRecieved - FrameNo); } }

    void BufferMessage(string op, Action<IReadableBuffer> handler)
    {
        sp.OnMessage(op, (data) =>
        {
            replayer.Record(op, data);
            msgQ.Enqueue(new KeyValuePair<string, Action>(op, () => { handler(data); }));
        });

        replayer.OnMessage(op, (data)=>
        {
            msgQ.Enqueue(new KeyValuePair<string, Action>(op, () => { handler(data); }));
        });
    }

    // 逻辑帧前进
    void OnFrameMoveForwardMsg(IReadableBuffer data)
    {
        //var no = data.ReadULong();
        //UnityEngine.Debug.Assert(no == frameNoRecieved + 1);
        //frameNoRecieved = no;
        frameNoRecieved++;
        msgQ.Enqueue(new KeyValuePair<string, Action>("FrameEnd", null));
    }

    int te = 0;
    public void OnTimeElapsed(int timeElapsed)
    {
        if (!Started)
            return;

        te += timeElapsed;
        var fact = WaitingFrameCount < 1 ? 1 : WaitingFrameCount;
        te *= fact;
        MapUnit.SpeedUp = fact;

        while (te >= Room.FrameInterval)
        {
            te -= Room.FrameInterval;
            MoveOneStep();
        }
    }

    public override void MoveOneStep()
    {
        if (FrameNo >= frameNoRecieved || Finished)
            return;

        // 把这一帧所有的命令先执行了
        while (msgQ.Count > 0)
        {
            var kv = msgQ.Dequeue();
            if (kv.Key == "FrameEnd" || kv.Key == "BattleEnd")
            {
                base.MoveOneStep();
                OnFrameElapsed.SC(this);

                break;
            }
            else
                kv.Value();
        }
    }

    public int WinnerAward = 0;
    public int LoserAward = 0;
    public string[] WinnerAwardUnits = null;

    // 战斗开始
    bool inReplay = false;
    void OnBattleBeginMsg(IReadableBuffer data)
    {
        frameNoRecieved = 0;
        msgQ.Clear();

        inReplay = replayer.InReplaying;
        if (!inReplay)
        {
            replayer.Clear();
            replayer.Record("BattleBegin", data);
        }

        Clear();

        var randomSeed = data.ReadInt();
        var lvID = data.ReadString();
        WinnerAward = data.ReadInt();
        LoserAward = data.ReadInt();
        WinnerAwardUnits = data.ReadStringArr();
        var roomID = data.ReadString();
        var usrs = data.ReadStringArr();
        var usrsInfo = data.ReadArr<UserInfo>();
        var sz = data.ReadVec2();

        // 设置当前用户信息
        var gc = GameCore.Instance;
        gc.CurrentRoom = this;
        var meID = gc.MeID;
        if (usrs[1] == meID)
            gc.MePlayer = 1;
        else if (usrs[2] == meID)
            gc.MePlayer = 2;
        else
            gc.MePlayer = 0; // 观战者

        // 初始化房间信息
        var usr1 = usrs[1];
        var usr2 = usrs[2];
        OnBeforeBattleBegin.SC(usr1, usr2, sz, inReplay);
        Init(roomID, sz, lvID, usr1, usr2, usrsInfo[1], usrsInfo[2]);
        base.BattleBegin(randomSeed);

        OnBattleBegin.SC(this, inReplay);
    }

    // 战斗结束消息
    void OnBattleEndMsg(IReadableBuffer data)
    {
        var winner = data.ReadString();
        frameNoRecieved++;
        msgQ.Enqueue(new KeyValuePair<string, Action>("BattleEndFrame", () => { ProcessBattleEndFrame(winner); }));
    }

    // 战斗结束最后一帧
    void ProcessBattleEndFrame(string winner)
    {
        BattleEnd(winner);
        OnBattleEnd.SC(this, winner, inReplay);
    }

    public override void BattleEnd(string winner)
    {
        base.BattleEnd(winner);

        // 录像就不做战斗后结算
        if (inReplay)
            return;

        // 战斗后结算
        var meInfo = GameCore.Instance.MeInfo;

        // 战斗奖励
        meInfo.Coins += winner == GameCore.Instance.MeID ? WinnerAward : LoserAward;

        // PVP 胜负场统计
        if (IsPVP)
        {
            if (winner == GameCore.Instance.MeID)
                meInfo.WinCount++;
            else if (winner != null)
                meInfo.LoseCount++;
        }
        else if (winner == GameCore.Instance.MeID) // PVE 通关记录
            meInfo.AddPassedPVELevel(Lv.LevelID);
    }

    // 增加指定资源
    protected override void NotifyResourceChanged(int p, string resourceType, Fix64 delta, Fix64 total)
    {
        OnResourceChanged.SC(p, resourceType, total);
    }

    // 通知资源生产
    public override void OnProduceResource(Unit u, string resType, Fix64 num)
    {
        OnResourceProduced.SC(u, resType, num);
    }

    // 开始建造战斗单位
    public override void OnConstructingBattleUnitStarted(Unit building, string genType)
    {
        base.OnConstructingBattleUnitStarted(building, genType);
        OnStartConstructingBattleUnit.SC(building, genType);
    }

    // 设置行进目标
    void OnSetPathMsg(IReadableBuffer data)
    {
        var uid = data.ReadString();
        var path = data.ReadVec2Arr();
        var u = GetUnit(uid);
        u.IgnoreTargetWhenMoving = data.ReadBool();
        u.ResetPath(path);
        OnSetPath.SC(u);
    }

    // 开始建造一个建筑单位
    void OnCosntructBuildingUnitMsg(IReadableBuffer data)
    {
        var constructUnitType = data.ReadString();
        var player = data.ReadInt();
        var pos = data.ReadVec2();

        ConstructBuilding(constructUnitType, pos, player);
    }

    // 取消建筑建造
    void OnCancelBuilding(IReadableBuffer data)
    {
        var uid = data.ReadString();
        var u = GetUnit(uid);
        if (u == null)
            return;

        CancelBuilding(u);
    }

    // 开始建筑升级
    void OnBuildingLevelUpMsg(IReadableBuffer data)
    {
        var uid = data.ReadString();
        var toType = data.ReadString();
        if (!BuildingLevelUp(uid, toType))
            return;
    }

    // 开始建造一个战斗单位
    void OnConstructBattleUnitMsg(IReadableBuffer data)
    {
        var buildingUID = data.ReadString();
        var genType = data.ReadString();

        var u = GetUnit(buildingUID);
        u.Room.ConstructBattleUnit(u, genType);
    }

    // 通知部队单位进入建造列表
    public override void NotifyConstructingWaitingListChanged(Unit building, string genType)
    {
        OnConstructingWaitingListChanged.SC(building, genType);
    }

    // 开始建造挂件
    void OnConstructAccessaryUnit(IReadableBuffer data)
    {
        var buildingUID = data.ReadString();
        var genType = data.ReadString();
        var u = GetUnit(buildingUID);
        u.Room.ConstructAccessoryUnit(u, genType);
    }

    // 投放伞兵
    void OnDropSoldierFromCarrierMsg(IReadableBuffer data)
    {
        var player = data.ReadInt();
        var dropPt = data.ReadVec2();
        if (!CreateSoldierCarrier(player, dropPt))
            return;
    }

    // 直接添加单位
    void OnAddUnitAtMsg(IReadableBuffer data)
    {
        var player = data.ReadInt();
        var type = data.ReadString();
        var num = data.ReadInt();
        var pos = data.ReadVec2();
        FC.For(num, (i) => { AddNewUnit(null, type, pos, player); });
    }

    // 交换 usr1/usr2 对应的 player number
    void OnPlayersSwitchedMsg(IReadableBuffer data)
    {
        var me = GameCore.Instance.MePlayer;
        GameCore.Instance.MePlayer = me == 1 ? 2 : 1;
        OnUsrsSwitched.SC();
    }

    void OnReleaseBattleUnitsMsg(IReadableBuffer data)
    {
        var uid = data.ReadString();
        var u = GetUnit(uid);
        var pt = data.ReadVec2();
        ReleaseBattleUnits(u, pt);
        OnConstructingWaitingListChanged.SC(u, null);
    }

    // 作弊代码
    void OnCheatCodeMsg(IReadableBuffer data)
    {
        var code = data.ReadString();
        switch (code)
        {
            case "ShowMeTheMoney":
                {
                    var uid = data.ReadString();
                    var num = data.ReadInt();
                    var p = GetNoByUser(uid);
                    var money = Players[p].Resources["Money"];
                    money += num;
                    Players[p].Resources["Money"] = money;
                    OnResourceChanged.SC(p, "Money", money);
                }
                break;
            default:
                throw new Exception("do not support any cheat code yet");
        }
    }

    // 通知建筑完成
    public override void OnBuildingConstructingCompleted(Unit u)
    {
        base.OnBuildingConstructingCompleted(u);
        OnConstructingCompleted.SC(u);
    }

    // 通知开始升级
    public override void OnBuildingLevelUp(Unit u)
    {
        base.OnBuildingLevelUp(u);
        NotifyBuildingLevelUp.SC(u);
    }

    // 添加地图障碍
    protected override void OnObstacleAdded(Vec2[] vertices)
    {
        NotifyAddObstacle.SC(vertices);
    }

    protected override void OnUnitAdded(Unit building, Unit u)
    {
        if (u.cfg.IsBuilding)
            NotifyAddBuildingUnit.SC(u);
        else
            NotifyAddBattleUnit.SC(building, u);
    }

    protected override void OnUnitRemoved(Unit u)
    {
        NotifyUnitRemoved.SC(u);
    }

    public bool IsMine(Unit u)
    {
        return u.Player != GameCore.Instance.MePlayer;
    }

    public Unit[] GetAllMyUnits(Func<Unit, bool> filter = null)
    {
        return GetAllUnitsByPlayer(GameCore.Instance.MePlayer, filter);
    }

    // 获取指定资源数量
    public Fix64 GetMyResource(string resourceType)
    {
        PlayerInfoInRoom pi = Players[GameCore.Instance.MePlayer];
        return pi.Resources.ContainsKey(resourceType) ? pi.Resources[resourceType] : 0;
    }

    public Unit[] GetMyUnitsByType(string unitType)
    {
        return GetUnitsByType(unitType, GameCore.Instance.MePlayer);
    }

    public Unit GetMyFirstUnitByType(string unitType)
    {
        foreach (var u in AllUnits)
        {
            if (u.UnitType == unitType && u.Player == GameCore.Instance.MePlayer)
                return u;
        }

        return null;
    }

    #region 战斗相关

    // 单体攻击
    public override void DoSingleAttack(Unit attacker, Unit target)
    {
        base.DoSingleAttack(attacker, target);
        OnDoAttack.SC(attacker, target);
    }

    // 圆形攻击
    public override void DoAOEAttackPie(Unit attacker, Unit target)
    {
        base.DoAOEAttackPie(attacker, target);
        OnDoAttack.SC(attacker, target);
    }

    // 扇形攻击
    public override void DoAOEAttackFan(Unit attacker, Unit target)
    {
        base.DoAOEAttackFan(attacker, target);
        OnDoAttack.SC(attacker, target);
    }

    #endregion

    // 客户端在战局内添加额外的状态机
    public void AddStateMachineInRoom(StateMachine sm)
    {
        smm.Add(sm);
    }
}
